use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Metrics {
    pub tool_calls: ToolCallMetrics,
    pub performance: PerformanceMetrics,
    pub errors: ErrorMetrics,
    pub system: SystemMetrics,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCallMetrics {
    pub total_calls: u64,
    pub successful_calls: u64,
    pub failed_calls: u64,
    pub calls_by_tool: std::collections::HashMap<String, u64>,
    pub average_response_time_ms: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
    pub uptime_seconds: u64,
    pub memory_usage_mb: f64,
    pub cpu_usage_percent: f64,
    pub active_connections: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorMetrics {
    pub connection_errors: u64,
    pub timeout_errors: u64,
    pub validation_errors: u64,
    pub server_errors: u64,
    pub last_error_time: Option<i64>,
    pub last_error_message: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemMetrics {
    pub tms_server_connected: bool,
    pub last_health_check: Option<i64>,
    pub health_check_failures: u64,
    pub version: String,
    pub start_time: i64,
}

pub struct MetricsCollector {
    metrics: Arc<RwLock<Metrics>>,
    start_time: Instant,
    
    // Atomic counters for thread-safe updates
    total_calls: AtomicU64,
    successful_calls: AtomicU64,
    failed_calls: AtomicU64,
    connection_errors: AtomicU64,
    timeout_errors: AtomicU64,
    validation_errors: AtomicU64,
    server_errors: AtomicU64,
    health_check_failures: AtomicU64,
}

impl MetricsCollector {
    pub fn new(version: String) -> Self {
        let start_time = Instant::now();
        let metrics = Metrics {
            tool_calls: ToolCallMetrics {
                total_calls: 0,
                successful_calls: 0,
                failed_calls: 0,
                calls_by_tool: std::collections::HashMap::new(),
                average_response_time_ms: 0.0,
            },
            performance: PerformanceMetrics {
                uptime_seconds: 0,
                memory_usage_mb: 0.0,
                cpu_usage_percent: 0.0,
                active_connections: 0,
            },
            errors: ErrorMetrics {
                connection_errors: 0,
                timeout_errors: 0,
                validation_errors: 0,
                server_errors: 0,
                last_error_time: None,
                last_error_message: None,
            },
            system: SystemMetrics {
                tms_server_connected: false,
                last_health_check: None,
                health_check_failures: 0,
                version,
                start_time: chrono::Utc::now().timestamp(),
            },
        };

        Self {
            metrics: Arc::new(RwLock::new(metrics)),
            start_time,
            total_calls: AtomicU64::new(0),
            successful_calls: AtomicU64::new(0),
            failed_calls: AtomicU64::new(0),
            connection_errors: AtomicU64::new(0),
            timeout_errors: AtomicU64::new(0),
            validation_errors: AtomicU64::new(0),
            server_errors: AtomicU64::new(0),
            health_check_failures: AtomicU64::new(0),
        }
    }

    pub async fn record_tool_call(&self, tool_name: &str, duration: Duration, success: bool) {
        self.total_calls.fetch_add(1, Ordering::Relaxed);
        
        if success {
            self.successful_calls.fetch_add(1, Ordering::Relaxed);
        } else {
            self.failed_calls.fetch_add(1, Ordering::Relaxed);
        }

        let mut metrics = self.metrics.write().await;
        
        // Update tool-specific counters
        *metrics.tool_calls.calls_by_tool.entry(tool_name.to_string()).or_insert(0) += 1;
        
        // Update average response time (simple moving average)
        let total_calls = self.total_calls.load(Ordering::Relaxed) as f64;
        let current_avg = metrics.tool_calls.average_response_time_ms;
        let new_duration_ms = duration.as_millis() as f64;
        
        metrics.tool_calls.average_response_time_ms = 
            (current_avg * (total_calls - 1.0) + new_duration_ms) / total_calls;
    }

    pub fn record_connection_error(&self) {
        self.connection_errors.fetch_add(1, Ordering::Relaxed);
    }

    pub fn record_timeout_error(&self) {
        self.timeout_errors.fetch_add(1, Ordering::Relaxed);
    }

    pub fn record_validation_error(&self) {
        self.validation_errors.fetch_add(1, Ordering::Relaxed);
    }

    pub fn record_server_error(&self, message: &str) {
        self.server_errors.fetch_add(1, Ordering::Relaxed);
        
        tokio::spawn({
            let metrics = self.metrics.clone();
            let message = message.to_string();
            async move {
                let mut m = metrics.write().await;
                m.errors.last_error_time = Some(chrono::Utc::now().timestamp());
                m.errors.last_error_message = Some(message);
            }
        });
    }

    pub async fn update_system_status(&self, tms_connected: bool, health_check_success: bool) {
        let mut metrics = self.metrics.write().await;
        
        metrics.system.tms_server_connected = tms_connected;
        metrics.system.last_health_check = Some(chrono::Utc::now().timestamp());
        
        if !health_check_success {
            self.health_check_failures.fetch_add(1, Ordering::Relaxed);
        }
    }

    pub async fn get_metrics(&self) -> Metrics {
        let mut metrics = self.metrics.read().await.clone();
        
        // Update atomic counters
        metrics.tool_calls.total_calls = self.total_calls.load(Ordering::Relaxed);
        metrics.tool_calls.successful_calls = self.successful_calls.load(Ordering::Relaxed);
        metrics.tool_calls.failed_calls = self.failed_calls.load(Ordering::Relaxed);
        
        metrics.errors.connection_errors = self.connection_errors.load(Ordering::Relaxed);
        metrics.errors.timeout_errors = self.timeout_errors.load(Ordering::Relaxed);
        metrics.errors.validation_errors = self.validation_errors.load(Ordering::Relaxed);
        metrics.errors.server_errors = self.server_errors.load(Ordering::Relaxed);
        
        metrics.system.health_check_failures = self.health_check_failures.load(Ordering::Relaxed);
        
        // Update performance metrics
        metrics.performance.uptime_seconds = self.start_time.elapsed().as_secs();
        
        // Get system resource usage (simplified - in production, use proper system monitoring)
        metrics.performance.memory_usage_mb = get_memory_usage();
        metrics.performance.cpu_usage_percent = get_cpu_usage();
        
        metrics
    }

    pub async fn get_metrics_json(&self) -> String {
        let metrics = self.get_metrics().await;
        serde_json::to_string_pretty(&metrics).unwrap_or_else(|_| "{}".to_string())
    }
}

// Simplified system resource monitoring
// In production, use proper system monitoring libraries
fn get_memory_usage() -> f64 {
    // Placeholder - implement actual memory monitoring
    0.0
}

fn get_cpu_usage() -> f64 {
    // Placeholder - implement actual CPU monitoring
    0.0
}

#[cfg(test)]
mod tests {
    use super::*;
    use tokio::time::{sleep, Duration};

    #[tokio::test]
    async fn test_metrics_collection() {
        let collector = MetricsCollector::new("test-version".to_string());
        
        // Record some tool calls
        collector.record_tool_call("list_vehicles", Duration::from_millis(100), true).await;
        collector.record_tool_call("create_order", Duration::from_millis(200), false).await;
        
        // Record some errors
        collector.record_connection_error();
        collector.record_server_error("Test error");
        
        // Get metrics
        let metrics = collector.get_metrics().await;
        
        assert_eq!(metrics.tool_calls.total_calls, 2);
        assert_eq!(metrics.tool_calls.successful_calls, 1);
        assert_eq!(metrics.tool_calls.failed_calls, 1);
        assert_eq!(metrics.errors.connection_errors, 1);
        assert_eq!(metrics.errors.server_errors, 1);
        assert!(metrics.tool_calls.calls_by_tool.contains_key("list_vehicles"));
        assert!(metrics.tool_calls.calls_by_tool.contains_key("create_order"));
    }
}